﻿using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using NewsGator.Social;
using NewsGator.Social.Data;
using NewsGator.Social.Library.Data;
using NewsGator.Social.Spotlight;
using SocialSites.ApplicationServices;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Serialization;

namespace NGPT.ExportToGephi
{
    /// <summary>
    /// Implements export logic using the Sitrion Social server side API.
    /// </summary>
    public class ExportService : IExportService    
    {
        private enum NodeType
        {
            Person,
            Community,
            Sphere,
            Discussion,
            Tag
        }

        private XmlWriter writer;
        private UserProfileManager profileManager;
        //private SocialProfileManager socialProfileManager;
        private SocialContextManager socialContextManager;
        private SocialProfileServiceClient socialProfileServiceClient;
        private SocialSpotlightClient socialSpotlightClient;
        private string creator;
        private string description;

        public ExportService(SPServiceContext serviceContext, string creator, string description)
        {
            this.creator = creator;
            this.description = description;

            profileManager = new UserProfileManager(serviceContext);
            //socialProfileManager = new SocialProfileManager();
            socialContextManager = new SocialContextManager();
            socialProfileServiceClient = new SocialProfileServiceClient(serviceContext);
            socialSpotlightClient = new SocialSpotlightClient();
        }

        private void WriteNode(string id, string label, NodeType type)
        {
            writer.WriteStartElement("node"); writer.WriteAttributeString("id", id); writer.WriteAttributeString("label", label);
                writer.WriteStartElement("attvalues");
                    writer.WriteStartElement("attvalue"); writer.WriteAttributeString("for", "0"); writer.WriteAttributeString("value", type.ToString());
                    writer.WriteEndElement();
                writer.WriteEndElement();
            writer.WriteEndElement();
        }

        private void WriteEdge(string id, string source, string target)
        {
            writer.WriteStartElement("edge"); writer.WriteAttributeString("id", id); writer.WriteAttributeString("source", source); writer.WriteAttributeString("target", target);
            writer.WriteEndElement();
        }


        /// <summary>
        /// Exports social network nodes to stream in GEXF format.
        /// </summary>
        /// <param name="stream">Stream to be used for export.</param>
        /// <param name="creator">Creator name.</param>
        /// <param name="description">Description.</param>
        /// <param name="includePeople">Whether to export people nodes.</param>
        /// <param name="includeCommunities">Whether to export communities nodes.</param>
        /// <param name="includeSpheres">Whether to export spheres nodes.</param>
        /// <param name="includeDiscussions">Whether to export item-level discussions nodes.</param>
        public void ExportToGEXF(Stream stream, bool includePeople, bool includePeopleExpertiseTags, bool includeCommunities, bool includeSpheres, bool includeDiscussions)
        {
            List<SocialGroup> groups = new List<SocialGroup>();

            // Communities
            if (includeCommunities)
            {
                var communities = socialContextManager.GetGroupsByType(SocialGroupType.SharePointWeb);
                groups.AddRange(communities);
            }

            // Spheres
            if (includeSpheres)
            {
                var spheres = socialContextManager.GetGroupsByType(SocialGroupType.StandAlone);
                groups.AddRange(spheres);
            }

            // Discussions
            if (includeDiscussions)
            {
                var discussions = socialContextManager.GetGroupsByType(SocialGroupType.Uri);
                groups.AddRange(discussions);
            }
            using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
            {
                ZipArchiveEntry gexfEntry = archive.CreateEntry("SitrionSocialNetwork.gexf");
                using (writer = XmlWriter.Create(gexfEntry.Open()))
                {
                    // Root
                    writer.WriteStartDocument();
                    writer.WriteStartElement("gexf", "http://www.gexf.net/1.2draft"); writer.WriteAttributeString("version", "1.2");
                    writer.WriteStartElement("meta"); writer.WriteAttributeString("lastmodifieddate", DateTime.Now.ToIso8601String());
                    writer.WriteElementString("creator", creator);
                    writer.WriteElementString("description", description);
                    writer.WriteEndElement();

                    writer.WriteStartElement("graph"); writer.WriteAttributeString("mode", "static"); writer.WriteAttributeString("defaultedgetype", "directed");
                    writer.WriteStartElement("attributes"); writer.WriteAttributeString("class", "node");
                    writer.WriteStartElement("attribute"); writer.WriteAttributeString("id", "0"); writer.WriteAttributeString("title", "EntityType"); writer.WriteAttributeString("type", "string");
                    writer.WriteEndElement();
                    writer.WriteEndElement();

                    # region Nodes

                    writer.WriteStartElement("nodes");

                    // People
                    if (includePeople)
                    {
                        foreach (UserProfile profile in profileManager)
                        {
                            WriteNode(profile.AccountName, profile.DisplayName, NodeType.Person);

                            // People expertise tags
                            if (includePeopleExpertiseTags)
                            {
                                var userTags = socialSpotlightClient.GetTagsForUser(profile.AccountName, int.MaxValue);
                                foreach (var tag in userTags)
                                {
                                    WriteNode("_" + tag.Name, tag.Name, NodeType.Tag);
                                }
                            }

                        }
                    }


                    // Groups
                    foreach (var group in groups)
                    {
                        WriteNode(group.Id.ToString(), group.Name, GroupTypeToNodeType(group.SocialGroupType));
                    }

                    writer.WriteEndElement(); //close nodes

                    # endregion

                    # region Edges

                    int edgeCounter = 0;

                    writer.WriteStartElement("edges");

                    // User <-> User
                    if (includePeople)
                    {
                        foreach (UserProfile profile in profileManager)
                        {
                            foreach (var colleague in profile.Colleagues.GetItems())
                            {
                                WriteEdge((edgeCounter++).ToString(), profile.AccountName, colleague.Profile.AccountName);
                            }

                            // People expertise tags
                            if (includePeopleExpertiseTags)
                            {
                                var userTags = socialSpotlightClient.GetTagsForUser(profile.AccountName, int.MaxValue);
                                foreach (var tag in userTags)
                                {
                                    WriteEdge((edgeCounter++).ToString(), profile.AccountName, "_" + tag.Name);
                                }
                            }

                        }


                        // Groups <-> Users
                        if (groups.Count > 0)
                        {
                            foreach (var group in groups)
                            {
                                var members = socialProfileServiceClient.GetGroupMembershipUsers(group.Id);

                                foreach (var member in members)
                                {
                                    WriteEdge((edgeCounter++).ToString(), member.AccountName, group.Id.ToString());
                                }
                            }
                        }

                    }

                    writer.WriteEndElement(); // close edges

                    # endregion

                    writer.WriteEndElement(); // close graph
                    writer.WriteEndElement(); // close gexf
                    writer.WriteEndDocument();

                    writer.Flush();
                }
            }
        }

        private NodeType GroupTypeToNodeType(SocialGroupType socialGroupType)
        {
            switch (socialGroupType)
            {
                case SocialGroupType.SharePointWeb:
                    return NodeType.Community;

                case SocialGroupType.StandAlone:
                    return NodeType.Sphere;

                case SocialGroupType.Uri:
                    return NodeType.Discussion;

                default:
                    throw new ArgumentException("Unknown group type: " + socialGroupType.ToString());
            }
        }
    }
}
